<?php
include_once __DIR__ . "/../base.php";
class hsty extends base
{
    // protected $xml;
    // protected $head;

    // protected $channel_type = "WX";

    // protected $appid;

    // protected $IsvOrgId;


    // protected $provider_type;


    protected $sign_type = "";
    // protected $callback_url = "";
    protected $md5_key = "";
    protected $publicKey = "";
    protected $privateKey = "";
    protected $merchant_id = "";




    public function __construct($setting)
    {
        // parent::__construct($setting);
        // // $this->xml = new xml();
        // $this->appid = $setting['appid'];  //应用id

        // $this->IsvOrgId = $setting['isv'];  //合作方机构号
        // $this->provider_type = $setting['provider_type'];



        $this->sign_type = $setting['sign_type'];
        $this->merchant_id = $setting['mchid'];  //商户号
        $this->callback_url = $setting['callback_url'] ?? "";
        $this->privateKey = $setting['privateKey'] ?? "";
        $this->publicKey = $setting['publicKey'] ?? "";
        $this->md5_key = $setting['md5_key'] ?? "";
    }


    // 提交支付
    public function submitOrder($data)
    {
        $order = [
            'service' => 'pay.weixin.jspay',
            'version' => '2.0',
            'is_raw' => 1,
            'sub_appid' => $data['SubAppId'],
            'mch_id' => $this->merchant_id,
            'nonce_str' => $this->getRandomString(),
            'body'             => $data['OutTradeNo'],
            // 'detail'           => $detail,
            'out_trade_no'     => $data['OutTradeNo'],
            'total_fee'        => $data['TotalAmount'], // 单位：分
            'mch_create_ip' => request()->ip(),
            'notify_url'         => $data['NotifyUrl'] ?? $this->callback_url,       // 你也可以在下单时单独设置来想覆盖它
            // 'trade_type' => 'JSAPI',
            "is_minipg" => 1,
            'sub_openid' => $data['OpenId']
        ];
        $order['sign_type'] = $this->sign_type;
        if ($this->sign_type == 'RSA_1_1' || $this->sign_type == 'RSA_1_256') {
            $order['sign'] = $this->MakeSign_swift_rsa($order);
        } else {
            // $file_name = $_SERVER['DOCUMENT_ROOT'] . "/../api_resource/logs/" . date('Ymd') . "_pay.log";

            // file_put_contents($file_name, date('Y-m-d H:i:s') . "--------swt111请求报文：---------", FILE_APPEND);
            // file_put_contents($file_name, "\r\n", FILE_APPEND);
            // file_put_contents($file_name, "md5", FILE_APPEND);
            // file_put_contents($file_name, "\r\n", FILE_APPEND);
            $order['sign'] = $this->MakeSign_swift($order);
        }
        $order_xml = $this->ToXml($order);
        // var_dump($order_xml);
        $response = $this->postXmlCurl($order_xml, "https://pay.hstypay.com/v2/pay/gateway", false, 30);

        $response_arr = $this->FromXml($response);
        $file_name = $_SERVER['DOCUMENT_ROOT'] . "/../api_resource/logs/" . date('Ymd') . "_pay.log";

        file_put_contents($file_name, date('Y-m-d H:i:s') . "--------hsty请求报文：---------", FILE_APPEND);
        file_put_contents($file_name, "\r\n", FILE_APPEND);
        file_put_contents($file_name, json_encode($order), FILE_APPEND);
        file_put_contents($file_name, "\r\n", FILE_APPEND);


        file_put_contents($file_name, date('Y-m-d H:i:s') . "--------hsty响应报文：---------", FILE_APPEND);
        file_put_contents($file_name, "\r\n", FILE_APPEND);
        file_put_contents($file_name, json_encode($response_arr), FILE_APPEND);
        file_put_contents($file_name, "\r\n", FILE_APPEND);
        if ($response_arr['status'] == 0) {
            $json = json_decode($response_arr['pay_info'], true);
            // 处理时间 timeStamp
            $json['timestamp'] = $json['timeStamp'];
            return array('status' => 1, 'payInfo' => $json);
        } else {
            if (isset($response_arr['status'])) {
                return ['status' => 0, 'msg' => "下单失败:" . $response_arr['message']];
            }
        }
    }
    protected function getXmlEncode($xml)
    {
        $ret = preg_match("/<?xml[^>]* encoding=\"(.*)\"[^>]* ?>/i", $xml, $arr);
        if ($ret) {
            return strtoupper($arr[1]);
        } else {
            return "";
        }
    }
    // 回调
    public function notifyOrder()
    {
        $resultXml = file_get_contents('php://input');
        // file_put_contents('1.txt',$xml);//检测是否执行callback方法，如果执行，会生成1.txt文件，且文件中的内容就是通知参数
        $xml = simplexml_load_string($resultXml);
        $encode = $this->getXmlEncode($resultXml);
        $parameters = [];
        if ($xml && $xml->children()) {
            foreach ($xml->children() as $node) {
                //有子节点
                if ($node->children()) {
                    $k = $node->getName();
                    $nodeXml = $node->asXML();
                    $v = substr($nodeXml, strlen($k) + 2, strlen($nodeXml) - 2 * strlen($k) - 5);
                } else {
                    $k = $node->getName();
                    $v = (string)$node;
                }

                if ($encode != "" && $encode != "UTF-8") {
                    $k = iconv("UTF-8", $encode, $k);
                    $v = iconv("UTF-8", $encode, $v);
                }
                $parameters[$k] = $v;
            }
        }
        $resData = [
            'code' => 200,
            "data" => [],
            "msg" => "",
            "resp" => 'fail'
        ];
        // 验证签名
        $swiftpassSign = strtoupper($parameters["sign"]);
        $sign = "";
        if ($parameters['sign_type'] == "MD5") {
            $sign = $this->MakeSign_swift($parameters);
        } else if ($parameters['sign_type'] == 'RSA_1_1' || $parameters['sign_type'] == 'RSA_1_256') {
            $sign = $this->MakeSign_swift_rsa_callback($parameters);
        }
        if ($sign != $swiftpassSign) {
            $resData['code'] = 500;
            $resData['msg'] = "签名不匹配,解密签名:" . $sign . "请求签名:" . $swiftpassSign;
        } else if ($parameters['status'] == 0 && $parameters['result_code'] == 0) {
            $resData["data"] = [
                "order_sn" =>  $parameters['out_trade_no'],
            ];
            $resData["resp"] = "success";
        }
        return $resData;
    }
    public function refund($order_sn, $refund_order_sn, $refund_price, $remark)
    {

        $refund_post_data = [
            'service' => 'unified.trade.refund',
            'version' => '2.0',
            'mch_id' => $this->merchant_id,
            'out_trade_no'             => $order_sn,
            'out_refund_no'           => $refund_order_sn,
            'total_fee'        => $refund_price, // 单位：分
            'refund_fee' => $refund_price,
            'op_user_id'         => $this->merchant_id,       // 你也可以在下单时单独设置来想覆盖它
            'nonce_str' => $this->getRandomString(),
        ];

        $file_name = $_SERVER['DOCUMENT_ROOT'] . "/../api_resource/logs/" . date('Ymd') . "_pay.log";

        file_put_contents($file_name, date('Y-m-d H:i:s') . "--------hsty11退款请求报文：---------", FILE_APPEND);
        file_put_contents($file_name, "\r\n", FILE_APPEND);
        file_put_contents($file_name, json_encode($refund_post_data), FILE_APPEND);
        file_put_contents($file_name, "\r\n", FILE_APPEND);

        $refund_post_data['sign_type'] = $this->sign_type;
        if ($this->sign_type == 'rsa') {
            $refund_post_data['sign'] = $this->MakeSign_swift_rsa($refund_post_data);
            //                file_put_contents(RUNTIME_PATH."swift_rsa.txt","order_data:".print_r($order,1)."\n",FILE_APPEND);
        } else {
            $refund_post_data['sign'] = $this->MakeSign_swift($refund_post_data);
        }

        $order_xml = $this->ToXml($refund_post_data);
        $response = $this->postXmlCurl($order_xml, "https://pay.hstypay.com/v2/pay/gateway", false, 30);
        $response_arr = $this->FromXml($response);

        // file_put_contents(RUNTIME_PATH . "swift_back_" . date("Ymd") . ".txt", "post_data:" . print_r($refund_post_data, 1) . "\n return_data:" . print_r($response_arr, 1), FILE_APPEND);

        file_put_contents($file_name, date('Y-m-d H:i:s') . "--------hsty11退款返回报文：---------", FILE_APPEND);
        file_put_contents($file_name, "\r\n", FILE_APPEND);
        file_put_contents($file_name, json_encode($response_arr), FILE_APPEND);
        file_put_contents($file_name, "\r\n", FILE_APPEND);
        $data = [
            "code" => 500,
            "data" => [],
            "msg" =>  ''
        ];
        if (is_array($response_arr)) {
            $result = [];
            //                file_put_contents(RUNTIME_PATH."swift_back_".date("Ymd").".txt","\n return_obj:".print_r($result,1),FILE_APPEND);
            foreach ($response_arr as $key => $val) {
                $result[$key] = $val;
                if ($key == 'result_code' && $val == 0) {
                    $data['code'] = 200;
                    $data['msg'] = $response_arr['message'] ?? '';
                    $data['data'] = [
                        "response_arr" => $response_arr
                    ];
                } elseif ($key == 'result_code' && $val == 1) {
                    $data['code'] = 500;
                    $data['msg'] = $response_arr['message'] ?? '';
                    $data['data'] = [
                        "response_arr" => $response_arr
                    ];
                }
            }
            //                file_put_contents(RUNTIME_PATH."swift_back_".date("Ymd").".txt","\n return_obj:".print_r($result,1),FILE_APPEND);
        }
        return $data;
    }
    public function MakeSign_swift_rsa_callback($arr)
    {

        $key = preg_replace('/(.{64})/', "$1\n", $this->publicKey);
        $public_key = '-----BEGIN PUBLIC KEY-----' . "\n" . $key . "\n" . '-----END PUBLIC KEY-----';

        $signPars = "";
        ksort($arr);
        foreach ($arr as $k => $v) {
            if ("sign" != $k && "" != $v) {
                $signPars .= $k . "=" . $v . "&";
            }
        }
        //            file_put_contents(RUNTIME_PATH."swift_rsa1.txt","arr:\n".json_encode($arr)."\n",FILE_APPEND);
        //            file_put_contents(RUNTIME_PATH."swift_rsa1.txt","public_key:\n".$public_key."\n",FILE_APPEND);
        //            file_put_contents(RUNTIME_PATH."swift_rsa1.txt","sign_str:\n".$signPars."\n",FILE_APPEND);

        $signPars = substr($signPars, 0, strlen($signPars) - 1);
        $res = openssl_get_publickey($public_key);
        if ($this->sign_type == 'RSA_1_1') {
            $result = (bool)openssl_verify($signPars, base64_decode($arr['sign']), $res);
            openssl_free_key($res);
        } else if ($this->sign_type == 'RSA_1_256') {
            $result = (bool)openssl_verify($signPars, base64_decode($arr['sign']), $res, OPENSSL_ALGO_SHA256);
            openssl_free_key($res);
        }
        if ($result == 1) {
            return $arr["sign"];
        }
        return 'error';
    }
    public static function FromXml($xml)
    {
        if (!$xml) {
            throw new \Exception("xml数据异常！");
        }
        //将XML转为array
        //禁止引用外部xml实体
        // libxml_disable_entity_loader(true);
        $arr = json_decode(json_encode(simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOCDATA)), true);
        return $arr;
    }
    protected static function postXmlCurl($xml, $url, $useCert = false, $second = 30)
    {
        $ch = curl_init();
        //设置超时
        curl_setopt($ch, CURLOPT_TIMEOUT, $second);

        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2); //严格校验
        //设置header
        curl_setopt($ch, CURLOPT_HEADER, FALSE);
        //要求结果为字符串且输出到屏幕上
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);

        if ($useCert == true) {
            //设置证书
            //使用证书：cert 与 key 分别属于两个.pem文件
            curl_setopt($ch, CURLOPT_SSLCERTTYPE, 'PEM');
            //            curl_setopt($ch,CURLOPT_SSLCERT, WxPayConfig::SSLCERT_PATH);
            curl_setopt($ch, CURLOPT_SSLKEYTYPE, 'PEM');
            //            curl_setopt($ch,CURLOPT_SSLKEY, WxPayConfig::SSLKEY_PATH);
        }
        //post提交方式
        curl_setopt($ch, CURLOPT_POST, TRUE);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
        //运行curl
        $data = curl_exec($ch);
        //返回结果
        if ($data) {
            curl_close($ch);
            return $data;
        } else {
            $error = curl_errno($ch);
            curl_close($ch);
            throw new \Exception("curl出错，错误码:$error");
        }
    }
    public static function ToXml($arr)
    {

        $xml = "<xml>";
        foreach ($arr as $key => $val) {
            if (is_numeric($val)) {
                $xml .= "<" . $key . ">" . $val . "</" . $key . ">";
            } else {
                $xml .= "<" . $key . "><![CDATA[" . $val . "]]></" . $key . ">";
            }
        }
        $xml .= "</xml>";
        return $xml;
    }
    public function MakeSign_swift_rsa($data)
    {
        $signPars = "";
        ksort($data);
        foreach ($data as $k => $v) {
            if ("" != $v && "sign" != $k) {
                $signPars .= $k . "=" . $v . "&";
            }
        }
        $signtype = $this->sign_type;
        $signPars = substr($signPars, 0, strlen($signPars) - 1);

        $key = preg_replace('/(.{64})/', "$1\n", $this->privateKey);
        $private_key = '-----BEGIN RSA PRIVATE KEY-----' . "\n" . $key . "\n" . '-----END RSA PRIVATE KEY-----';

        $res = openssl_get_privatekey($private_key);
        if ($signtype == 'RSA_1_1') {
            openssl_sign($signPars, $sign, $res);
        } else if ($signtype == 'RSA_1_256') {
            openssl_sign($signPars, $sign, $res, OPENSSL_ALGO_SHA256);
        }
        openssl_free_key($res);
        $sign = base64_encode($sign);
        //        file_put_contents(RUNTIME_PATH."swift_rsa.txt","signed:\n".$sign."\n",FILE_APPEND);
        return $sign;
    }
    public function MakeSign_swift($data)
    {

        //签名步骤一：按字典序排序参数
        ksort($data);
        $string = $this->ToUrlParams($data);
        //签名步骤二：在string后加入KEY
        $string = $string . "&key=" . $this->md5_key;
        //签名步骤三：MD5加密


        $string = md5(urldecode($string));

        //签名步骤四：所有字符转为大写
        $result = strtoupper($string);
        return $result;
    }
    public static function ToUrlParams($arr)
    {
        $buff = "";
        foreach ($arr as $k => $v) {
            if ($k != "sign" && $v != "" && !is_array($v)) {
                $buff .= $k . "=" . $v . "&";
            }
        }
        $buff = trim($buff, "&");
        return $buff;
    }
    public function getRandomString()
    {
        // mt_srand((float)microtime() * 10000); //optional for php 4.2.0 and up.
        $charid = strtoupper(md5(uniqid(rand(), true)));
        // $hyphen = chr(45); // "-"
        $uuid = substr($charid, 0, 8)
            . substr($charid, 8, 4)
            . substr($charid, 12, 4)
            . substr($charid, 16, 4)
            . substr($charid, 20, 12);
        return $uuid;
    }
}
